﻿namespace Suture.Tests
{
    using System;

    using NUnit.Framework;

    using Suture;
    using Suture.Exceptions;

    [TestFixture]
    public class BuilderTests
    {
        [Test]
        public void Should_build_type_with_parameterless_constructor()
        {
            Assert.That(Builder.For<object>().Build(), Is.Not.Null);
        }

        [Test]
        public void Should_use_greediest_constructor()
        {
            var builder = Builder.For<TwoConstructorClass>();
            TwoConstructorClass result = builder.Build();

            Assert.That(result.Constructor2Called, Is.True);
        }

        [Test]
        public void Should_use_arguments_when_provided()
        {
            var result = Builder.For<TwoConstructorClass>()
                                .WithFoo("SomeValue")
                                .Build() as TwoConstructorClass;

            Assert.That(result.Foo, Is.EqualTo("SomeValue"));
        }

        [Test]
        public void Should_exclude_constructors_that_do_not_include_parameters_specified_in_builder()
        {
            //the greediest constructor doesn't get called here as it doesn't have an argument named 'Foo'
            ThreeConstructorClass result = Builder
                                            .For<ThreeConstructorClass>()
                                            .WithFoo("SomeValue")
                                            .Build();

            Assert.That(result.Constructor2Called, Is.True);
        }

        [Test]
        public void Should_create_proxies_for_inteface_dependencies()
        {
            var builder = Builder.For<ClassWithInterfaceAsDependency>().Build() as ClassWithInterfaceAsDependency;
            Assert.That(builder.Disposable, Is.Not.Null);
        }

        [Test]
        public void Should_create_instance_using_explicit_constructor()
        {
            ThreeConstructorClass result =
                Builder.For<ThreeConstructorClass>().UsingConstructor<string>().Build();

            Assert.That(result.Constructor2Called, Is.True);
        }

        [Test]
        [ExpectedException(typeof(ConstructorResolutionException), ExpectedMessage = "Could not find a constructor that matched the parameters System.Int32, System.String")]
        public void Should_throw_if_cannot_find_matching_explicit_constructor()
        {
            Builder.For<ThreeConstructorClass>().UsingConstructor<int, string>().Build();
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot access public property members until Build() has been called.")]
        public void Should_not_be_able_to_access_builder_members_until_instance_has_been_built()
        {
            var builder = Builder.For<ClassWithInterfaceAsDependency>();
            var result = builder.Disposable;
        }

        [Test]
        public void Should_be_able_to_get_stub_using_public_property_getter()
        {
            var builder = Builder.For<ClassWithInterfaceAsDependency>();
            builder.Build();
            Assert.That((IDisposable)builder.Disposable, Is.Not.Null);
        }

        // ReSharper disable ClassNeverInstantiated.Local
        private class TwoConstructorClass
        {
            public bool Constructor1Called { get; private set; }
            public bool Constructor2Called { get; private set; }
            public string Foo { get; private set; }

            public TwoConstructorClass()
            {
                Constructor1Called = true;
            }

            public TwoConstructorClass(string foo)
            {
                Constructor2Called = true;
                Foo = foo;
            }
        }

        private class ThreeConstructorClass
        {
            public bool Constructor1Called { get; private set; }

            public bool Constructor2Called { get; private set; }

            public bool Constructor3Called { get; private set; }

            public string Foo { get; set; }

            public string Bah { get; set; }

            public string Qux { get; set; }

            public ThreeConstructorClass()
            {
                this.Constructor1Called = true;
            }

            public ThreeConstructorClass(string foo)
            {
                this.Foo = foo;
                this.Constructor2Called = true;
            }

            public ThreeConstructorClass(string bah, string qux)
            {
                this.Bah = bah;
                this.Qux = qux;
                this.Constructor3Called = true;
            }
        }

        private class ClassWithInterfaceAsDependency
        {
            public ClassWithInterfaceAsDependency(IDisposable disposable)
            {
                Disposable = disposable;
            }

            public IDisposable Disposable { get; private set; }
        }

        // ReSharper restore ClassNeverInstantiated.Local
    }
}
